Home > CS2640: Computer Organization and Assembly Programming > ProceduresProcedures
jal
and jr
Format:
jal <proc>
jal
: Call a procedure
$ra
to current PC before jumping to the label.$ra
, the return address, is register 31 by convention.jr
Format:
jr $ra
, the return addressjr
: Return from a procedure
$ra
jal
to call
procedures{
main ("enter n")
printint n = getint();
}
void prints(s) { output s; }
int getint() { read number from keyboard }
.data
.asciiz "enter n"
prommpt .text
main:
la $a0, prompt
jal print
jal getint
# Exit
li $v0, 10
syscall
# End of main
print:
li $v0, 0
syscall
jr $ra
# End of print
getint:
li $v0, 5
syscall
jr $ra
# End of getint
$a0
—$a3
: Parameters
$v0
—$v1
: Return values
$ra
: Return address
$t0
—$t7
: Temporary registers
$s0
—$s7
: Saved registers
Convention: Always start from the lowest register.
$a0
, not $a1
or $a2
, etc.)Convention: Procedure should not modify $a0
Note: Calling sub-programs from within subprograms
$t0
as a loop-control variable that calls another
procedure.$t0
before and after each call.proc:
# Push s0 to stack
addiu $s0, $sp, -4
sw $s0, $sp
# While Loop
li $s0, 1
while:
jal proc2
add $s0, 1
b while
end:
# Pop s0 from stack
lw $s0, ($sp)
addiu $sp, $sp, 4
jr $ra
# End of program
When main
is called, $a0
equivalent to C’s argc
while $a1
is equivalent to C’s argv
.
$a0
: Number of command-line arguments.$a1
: Array of pointers to strings.main:
# Load address of first arg
lw $a0, ($a1)
# Print it
li $v0, 4
syscall
# Load address of second arg
lw $a0, 4($a1)
# Print it
li $v0, 4
syscall
# Etc...
Note: The zeroth element of the argument array (
0($a1)
) is the program’s name.
To convert a single character to an integer, you must do math to the ASCII code.
In ASCII, numbers start at value 48, or char '0'
, so:
\boxed{ \text{Numeric Value of ASCII Char: } \text{c} - '0' }
Suppose we advance through the string to the next char in the string.
Note: Other Number Systems
- To convert a string to other number systems, instead of multiplying by 10, multiply it by the number system’s base.
- Ex: If you’re converting to octal, you simply need to multiply by 8 instead if 10 (or; shift left by 3 bits).
# a0: String
# v0: Value
# v1: 0 if valid, 1 if invalid
parseint:
Lowercase to Uppercase:
andi $t0, $t0, 0xdf
Uppercase to Lowercase:
ori $t0, $t0, 0x20
Why?:
- 1000001 is the ASCII value for “A”
- 1100001 is the ASCII value for “a”
This one-bit difference can be observed for all ASCII chars A—Z, hence we just need to clear the bit 0100000 to convert a lowercase char to uppercase.
The bit mask to use is 1011111, or
0xdf
in hexadecimal, to convert a lower-case ASCII code to uppercase.The opposite of this mask is
0x20
, which we use with theori
command to convert an uppercase ASCII value to lowercase.
andi $t0, $t0, 0xdf
sub $t0, $t0, 'A'
add $t0, $t0, 10
Steps:
$a0
—$a3
)main:
# 1. Make room for 5 parameters
addiu $sp, $sp, -20
# 2. Set values
li $a0, 1
li $a1, 2
li $a2, 3
li $a3, 4
li $t0, 5
sw $t0, ($sp)
# 3. Call function
jal sum5
# 4. Readjust the stack
sw $v0, n
addiu $sp, $sp, 20
# Exit
li $v0, 10
syscall
sum5:
# Add first four registers
add $v0, $a0, $a1
add $v0, $v0, $a2
add $v0, $v0, $a3
# Add the fifth param (stack)
lw $t0, ($sp)
add $v0, $v0, $t0
jr $ra
# End
Frame Pointer ($fp
): Lets us keep track of where the frame of a procedure is.
jal
and whatever else to our heart’s content.Things we can store in the stack frame:
Example Stack Frame:
$a0
$a1
$a2
$a3
$p4
(not a real register)$fp
$s0
$s1
$ra
Note how everything can be accessed relative to
$fp
(e.g.,$p4
is4($fp)
)
main:
# Save the stack frame
addiu $sp, $sp, -4
sw $fp, ($sp)
move $fp, $sp
# Make room for s0, s1, and ra
addiu $sp, $sp, -12
# Save s0, s1, and ra
sw $s0, 8($sp)
sw $s1, 4($sp)
sw $ra, 0($sp)
# DO WHATEVER WE WANT HERE
# - We can jal, ra, and s0—s3 to our heart's content
# Restore stack frame
lw $s0, 8($sp)
lw $s1, 4($sp)
lw $ra, 0($sp)
lw $fp, ($fp)
addiu $sp, $sp, 16
jr $ra
# Goal: Do n = sum(1,2)
main:
addiu $sp, $sp, -12
li $t0, 1
sw $t0, 4($sp)
li $t0, 2
sw $t0, 0($sp)
jal sum
lw $t0, ($sp)
sw $t0, n
addiu $sp, $sp, 12
sum5:
lw $t0, 8($sp)
lw $t1, 4($sp)
add $t0, $t0, $t1
sw $t0, ($sp)
jr $ra
# End
Local Variable: Variable that’s only allocated while function is running. Our stack should look like this:Example: Creating local variables
int proc(int m, int n) {
int k;
int a[20];
}
$sp
$fp
proc:
# Set up the frame pointer (our anchor point)
addiu $sp, $sp, -4
sw $fp, ($sp)
move $fp, sp
# Make room for k and a
addiu $sp, $sp, -84
# k is at -4($fp)
# a is at -84($fp)
# - (specifically a[0])
jr $ra
# End
Variable Arguments: Functions that take a variable number of arguments essentially are functions that take optional arrays.
E.g.,
System.out.println(String, args...)
In MIPS convention, the required parameters will be a-registers while varargs will be on the stack.
.data
name: .asciiz "John"
age: .word "22"
str: .asciiz " ... "
.text
main:
la $a0, str
addiu $sp, $sp, -8
la $t0, name
sw $t0, 0($sp)
lw $t0, age
sw $t0, 4($sp)
jal printf
addiu $sp, $sp, 8
printf:
# push a0 to stack
# Do t1 = $sp + 4 (addiu)
# Move a0 into t0
# while (*t0 != != '\0')
# ^ Note: Use lb $t2, ($t0)
# if t2 == '%'
# t0++
# switch (*t0 (next char))
# case '%'
# Print '%'
# case 'd'
# Print ($t1) (remember to use lw)
# t1 += 4
# ^ we do this so that if there's another %d, we will use the next param
# case 's'
# Print ($t1)
# t1 += 4
# else
# Print $t2 (syscall 11)
# t0++
# pop a0
# End
Functions that take arrays with no sentinel values must also take the length of the array as an argument.Example: Sum of array contents
.data
array: .word 0:20
sum: .word 0
.text
main:
la $a0, array
li $a1, 20
jal sumArr
sw $v0, sum
# Do exit syscall
# sumArr(array, array_length)
sumArr:
move $t0, $a0
li $v0, 0 # Sum
li $t1, 0 # Counter
while:
bge $t1, $a1, endw
lw $t2, ($t0)
add $v0, $v0, $t2
addiu $t0, $t0, 4
addiu $t1, $t1, 1
b while
endw:
jr $ra
# End
Recursive Subprogram: Has:
Remember: A recursive subprogram must have at least one parameter that it used to detect the base case.
- If it lacks a parameter, it’s just a for loop.
int fact(int n) {
if (n == 0) {
return 1;
} else {
return n * fact(n-1);
}
}
$a0
).# a0: n
fact:
# Push a0 and ra
addi $sp, $sp, -8
sw $a0, 4($sp)
sw $ra, 0($sp)
# Base Case: Return 1 if n==0
bnez $a0, else
li $v0, 1
b endif
else:
# Recursive Case: Return n * fact(n-1)
sub $a0, $a0, 1 # Get fact(n-1)
jal fact
lw $a0, 4($sp) # Multiply by original n
mul $v0, $a0, $v0
b endif
endif:
lw $a0, 4($sp)
lw $ra, 0($sp)
addi $sp, $sp, 8
jr $ra
.data
fibs: .word 0, 1, 1, ..., 34
.text
main:
la $a0, fibs
li $a1, 10
li $a2, 8
jal lsearch
# Simple Non-Recursive Traversal
traverse:
# SAVE RA
move $t0, $a0
while: beqz $t0, endw
lw $a0, DATA($t0)
jalr $a1
lw $t0, NEXT($t0)
b while
endw:
$ POP RAjr $ra
# How to use it
main:
la $a0, jead
la $a1, print
jal traverse
print:
li $v0, 4
syscall
jr $a0